#ifndef ORG_WXW0303_COMMON_ON_EXCEPTION_H
#define ORG_WXW0303_COMMON_ON_EXCEPTION_H

#include <utility>
#include "exception-hack.h"
#include "preprocess.h"

template <typename F>
struct OnExceptionHelper {
    OnExceptionHelper(const F &f);
    OnExceptionHelper(F &&f);

    OnExceptionHelper(OnExceptionHelper &&other);
    OnExceptionHelper& operator= (OnExceptionHelper &&other);

    ~OnExceptionHelper();
private:
    F f_;
    unsigned int liveExceptionObjNum_;

    DENY_COPY(OnExceptionHelper);
    DENY_ASSIGN(OnExceptionHelper);
};

template <typename F>
struct OnNoExceptionHelper {
    OnNoExceptionHelper(const F &f);
    OnNoExceptionHelper(F &&f);

    OnNoExceptionHelper(OnNoExceptionHelper &&other);
    OnNoExceptionHelper& operator= (OnNoExceptionHelper &&other);

    ~OnNoExceptionHelper();
private:
    F f_;
    unsigned int liveExceptionObjNum_;

    DENY_COPY(OnNoExceptionHelper);
    DENY_ASSIGN(OnNoExceptionHelper);
};

template <typename F>
inline OnExceptionHelper<typename std::decay<F>::type>
makeOnException (F &&f)
{
    return OnExceptionHelper<typename std::decay<F>::type> (std::forward<F>(f));
}

template <typename F>
inline OnNoExceptionHelper<typename std::decay<F>::type>
makeOnNoException (F &&f)
{
    return OnNoExceptionHelper<typename std::decay<F>::type> (std::forward<F>(f));
}

namespace common {

namespace impl {

enum class OnExceptionHelperHelper {};

template <typename F>
inline OnExceptionHelper<typename std::decay<F>::type> operator* (OnExceptionHelperHelper,F &&f)
{
    return makeOnException(std::forward<F>(f));
}

enum class OnNoExceptionHelperHelper {};

template <typename F>
inline OnNoExceptionHelper<typename std::decay<F>::type> operator* (OnNoExceptionHelperHelper,F &&f)
{
    return makeOnNoException(std::forward<F>(f));
}

}

}

// 当退出 ON_EXCEPTION 所处作用域时,发生了异常,则会执行后续语句块.
#define ON_EXCEPTION \
    auto ANONYMOUS_NAME(OnExceptionObj) = ::common::impl::OnExceptionHelperHelper{} * [&] () -> void

// 当退出 ON_NOEXCEPTION 所处作用域时,没有发生异常,则会执行后续语句块.
#define ON_NOEXCEPTIN \
    auto ANONYMOUS_NAME(OnNoExceptionObj) = ::common::impl::OnNoExceptionHelperHelper{} * [&] () -> void


///////// OnExceptionHelper Begin///////////////////////////////////////////////

template <typename F>
inline OnExceptionHelper<F>::OnExceptionHelper(const F &f):
    f_(f),
    liveExceptionObjNum_(uncaughtExceptionsNum())
{
}

template <typename F>
inline OnExceptionHelper<F>::OnExceptionHelper(F &&f):
    f_(std::move(f)),
    liveExceptionObjNum_(uncaughtExceptionsNum())
{
}

template <typename F>
inline OnExceptionHelper<F>::~OnExceptionHelper()
{
    if (uncaughtExceptionsNum() > liveExceptionObjNum_)
        f_();
    return ;
}

template <typename F>
inline OnExceptionHelper<F>::OnExceptionHelper(OnExceptionHelper &&other):
    f_(std::move(other.f_)),
    liveExceptionObjNum_(other.liveExceptionObjNum_)
{
}

template <typename F>
inline OnExceptionHelper<F>& OnExceptionHelper<F>::operator = (OnExceptionHelper &&other)
{
    f_ = std::move(other.f_);
    liveExceptionObjNum_ = other.liveExceptionObjNum_;
    return *this;
}


///////// OnExceptionHelper End  ///////////////////////////////////////////////

///////// OnNoExceptionHelper Begin///////////////////////////////////////////////

template <typename F>
inline OnNoExceptionHelper<F>::OnNoExceptionHelper(const F &f):
    f_(f),
    liveExceptionObjNum_(uncaughtExceptionsNum())
{
}

template <typename F>
inline OnNoExceptionHelper<F>::OnNoExceptionHelper(F &&f):
    f_(std::move(f)),
    liveExceptionObjNum_(uncaughtExceptionsNum())
{
}

template <typename F>
inline OnNoExceptionHelper<F>::~OnNoExceptionHelper()
{
    if (uncaughtExceptionsNum() <= liveExceptionObjNum_)
        f_();
    return ;
}

template <typename F>
inline OnNoExceptionHelper<F>::OnNoExceptionHelper(OnNoExceptionHelper &&other):
    f_(std::move(other.f_)),
    liveExceptionObjNum_(other.liveExceptionObjNum_)
{
}

template <typename F>
inline OnNoExceptionHelper<F>& OnNoExceptionHelper<F>::operator = (OnNoExceptionHelper &&other)
{
    f_ = std::move(other.f_);
    liveExceptionObjNum_ = other.liveExceptionObjNum_;
    return *this;
}

///////// OnNoExceptionHelper End  ///////////////////////////////////////////////

#endif // ORG_WXW0303_COMMON_ON_EXCEPTION_H
